package com.practice.io;

import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

public class ZipFileIO {

	protected static final int DEFAULT_BUFFER_LENGTH = 0x1000;

	public static void main(String[] args) {
		try {
			zip("E:\\Temporary\\Test", "E:\\Temporary\\Test.zip");
		} catch (IOException ex) {
			printException(ex);
		}

		try {
			unzip("E:\\Temporary\\New Folder", "E:\\Temporary\\Test.zip");
		} catch (IOException ex) {
			printException(ex);
		}
	}

	public static void zip(String inputFilePath, String zipFilePath)
			throws FileNotFoundException, IOException {
		File inputFile = new File(inputFilePath);
		zip(inputFile, zipFilePath);
	}

	public static void zip(File inputFile, String zipFilePath)
			throws FileNotFoundException, IOException {
		File zipFile = new File(zipFilePath);
		zipFile.createNewFile();

		ZipOutputStream zipOutput = new ZipOutputStream(new FileOutputStream(
				zipFile));
		try {
			zip(inputFile, zipOutput, "");
			zipOutput.flush();
		} finally {
			closeResource(zipOutput);
		}
		printMessage("Compress completed.");
	}

	protected static void zip(File inputFile, ZipOutputStream zipOutput,
			String baseEntryName) throws IOException {
		printMessage("Compressing " + inputFile.getPath());
		String entryName;
		if (baseEntryName == null || baseEntryName.isEmpty()) {
			entryName = inputFile.getName();
		} else {
			entryName = baseEntryName + "/" + inputFile.getName();
		}

		if (inputFile.isDirectory()) {
			zipOutput.putNextEntry(new ZipEntry(entryName + "/"));
			File[] subFiles = inputFile.listFiles();
			for (File subFile : subFiles) {
				zip(subFile, zipOutput, entryName);
			}
		} else {
			zipOutput.putNextEntry(new ZipEntry(entryName));
			FileInputStream fileInput = null;
			try {
				fileInput = new FileInputStream(inputFile);
				int readLength = 0;
				byte[] buffer = new byte[DEFAULT_BUFFER_LENGTH];
				while ((readLength = fileInput.read(buffer)) > 0) {
					zipOutput.write(buffer, 0, readLength);
				}
			} catch (FileNotFoundException ex) {
				printException(ex);
			} catch (IOException ex) {
				printException(ex);
			} finally {
				closeResource(fileInput);
			}
		}
	}

	public static void unzip(String outputDirPath, String zipFilePath)
			throws FileNotFoundException, IOException {
		File outputDir = new File(outputDirPath);
		unzip(outputDir, zipFilePath);
	}

	public static void unzip(File outputDir, String zipFilePath)
			throws FileNotFoundException, IOException {
		if (outputDir.exists()) {
			outputDir.delete();
		}
		outputDir.mkdir();

		File zipFile = new File(zipFilePath);
		ZipInputStream zipInput = new ZipInputStream(new FileInputStream(
				zipFile));
		try {
			unzip(outputDir, zipInput);
		} finally {
			closeResource(zipInput);
		}
		printMessage("Uncompress completed.");
	}

	protected static void unzip(File outputDir, ZipInputStream zipInput)
			throws IOException {
		ZipEntry zipEntry = null;
		while ((zipEntry = zipInput.getNextEntry()) != null) {
			String entryName = zipEntry.getName();
			printMessage("Uncompressing " + entryName);
			if (zipEntry.isDirectory()) {
				File dir = new File(outputDir, entryName);
				dir.mkdir();
			} else {
				File file = new File(outputDir, entryName);
				file.createNewFile();

				FileOutputStream fileOutput = null;
				try {
					fileOutput = new FileOutputStream(file);
					int readLength = 0;
					byte[] buffer = new byte[DEFAULT_BUFFER_LENGTH];
					while ((readLength = zipInput.read(buffer)) > 0) {
						fileOutput.write(buffer, 0, readLength);
					}
					fileOutput.flush();
				} catch (FileNotFoundException ex) {
					printException(ex);
				} catch (IOException ex) {
					printException(ex);
				} finally {
					closeResource(fileOutput);
				}
			}
		}
	}

	private static void printMessage(String text) {
		System.out.println(text);
	}

	private static void printException(Throwable ex) {
		printMessage(ex.getMessage());
		for (StackTraceElement stackTraceElement : ex.getStackTrace()) {
			printMessage(stackTraceElement.toString());
		}
	}

	private static void closeResource(Closeable resource) {
		if (resource != null) {
			try {
				resource.close();
			} catch (IOException ex) {
				printException(ex);
			}
		}
	}
}
